package me.majiajie.pagerbottomtabstrip.internal;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import me.majiajie.pagerbottomtabstrip.ItemController;
import me.majiajie.pagerbottomtabstrip.MaterialMode;
import me.majiajie.pagerbottomtabstrip.ResourceTable;
import me.majiajie.pagerbottomtabstrip.item.BaseTabItem;
import me.majiajie.pagerbottomtabstrip.item.MaterialItemView;
import me.majiajie.pagerbottomtabstrip.listener.OnTabItemSelectedListener;
import me.majiajie.pagerbottomtabstrip.listener.SimpleTabItemSelectedListener;
import me.majiajie.pagerbottomtabstrip.util.CustomAnimatorStateChangedListener;
import me.majiajie.pagerbottomtabstrip.util.ElementUtil;
import me.majiajie.pagerbottomtabstrip.util.EventUtil;
import me.majiajie.pagerbottomtabstrip.util.MyValueAnimator;
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.Component.DrawTask;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.ComponentTransition;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.media.image.PixelMap;
import ohos.multimodalinput.event.TouchEvent;

/**
 * 存放 Material Design 风格按钮的水平布局
 */
public class MaterialItemLayout extends ComponentContainer implements ItemController, Component.EstimateSizeListener,
        ComponentContainer.ArrangeListener, DrawTask, Component.TouchEventListener {

    private final int DEFAULT_SELECTED = 0;

    private int MATERIAL_BOTTOM_NAVIGATION_ACTIVE_ITEM_MAX_WIDTH = 0;
    private int MATERIAL_BOTTOM_NAVIGATION_ITEM_MAX_WIDTH = 0;
    private int MATERIAL_BOTTOM_NAVIGATION_ITEM_MIN_WIDTH = 0;
    private int MATERIAL_BOTTOM_NAVIGATION_ITEM_HEIGHT = 0;

    private final List<MaterialItemView> mItems = new ArrayList<>();
    private final List<OnTabItemSelectedListener> mListeners = new ArrayList<>();
    private final List<SimpleTabItemSelectedListener> mSimpleListeners = new ArrayList<>();

    private boolean mItemTintIcon;
    private int mItemDefaultColor;

    private int[] mTempChildWidths;
    private int mItemTotalWidth;

    private int mSelected = -1;
    private int mOldSelected = -1;

    private boolean mHideTitle;

    // 切换背景颜色时使用
    private final int ANIM_TIME = 300;
    private int mInterpolator;
    private boolean mChangeBackgroundMode;
    private List<Integer> mBackgroundColors;
    private List<Oval> mOvals;
    private RectFloat mTempRectF;
    private Paint mPaint;

    // 最后手指抬起的坐标
    private float mLastUpX;
    private float mLastUpY;

    public MaterialItemLayout(Context context) {
        this(context, null);
    }

    public MaterialItemLayout(Context context, AttrSet attrs) {
        this(context, attrs, null);
    }

    public MaterialItemLayout(Context context, AttrSet attrs, String defStyleAttr) {
        super(context, attrs, defStyleAttr);

        MATERIAL_BOTTOM_NAVIGATION_ACTIVE_ITEM_MAX_WIDTH = (int) Utils.getDimen(context, ResourceTable.Float_material_bottom_navigation_active_item_max_width);
        MATERIAL_BOTTOM_NAVIGATION_ITEM_MAX_WIDTH = (int) Utils.getDimen(context, ResourceTable.Float_material_bottom_navigation_item_max_width);
        MATERIAL_BOTTOM_NAVIGATION_ITEM_MIN_WIDTH = (int) Utils.getDimen(context, ResourceTable.Float_material_bottom_navigation_item_min_width);
        MATERIAL_BOTTOM_NAVIGATION_ITEM_HEIGHT = (int) Utils.getDimen(context, ResourceTable.Float_material_bottom_navigation_height);

        // 材料设计规范限制最多只能有5个导航按钮
        mTempChildWidths = new int[5];
        setEstimateSizeListener(this);
        setArrangeListener(this);
        addDrawTask(this, BETWEEN_BACKGROUND_AND_CONTENT);
        setTouchEventListener(this);
    }

    /**
     * 初始化方法
     *
     * @param items                按钮集合
     * @param checkedColors        选中颜色的集合
     * @param mode                 {@link MaterialMode}
     * @param animateLayoutChanges 是否应用默认的布局动画
     * @param doTintIcon           item是否需要对图标染色
     * @param color                item的默认状态颜色
     */
    public void initialize(List<MaterialItemView> items, List<Integer> checkedColors, int mode, boolean animateLayoutChanges, boolean doTintIcon, int color) {
        if (animateLayoutChanges) {
            setComponentTransition(new ComponentTransition());
        }

        mItems.clear();
        mItems.addAll(items);

        mItemTintIcon = doTintIcon;
        mItemDefaultColor = color;
        mHideTitle = (mode & MaterialMode.HIDE_TEXT) > 0;
        mChangeBackgroundMode = (mode & MaterialMode.CHANGE_BACKGROUND_COLOR) > 0;

        // 判断是否需要切换背景
        if (mChangeBackgroundMode) {
            // 初始化一些成员变量
            mOvals = new ArrayList<>();
            mBackgroundColors = checkedColors;
            mInterpolator = Animator.CurveType.ACCELERATE_DECELERATE;
            mTempRectF = new RectFloat();
            mPaint = new Paint();
            // 设置默认的背景
            setBackground(ElementUtil.getShapeElementWith6(mBackgroundColors.get(DEFAULT_SELECTED)));
        } else {
            // 设置按钮点击效果
            for (int i = 0; i < mItems.size(); i++) {
                MaterialItemView materialItemView = mItems.get(i);
                materialItemView.setRippleColor(0xFFFFFF & checkedColors.get(i) | 0x22000000);
            }
        }

        // 设置按钮点击效果
        for (int i = 0; i < mItems.size(); i++) {
            MaterialItemView materialItemView = mItems.get(i);
            materialItemView.isRipple(!mChangeBackgroundMode);
        }

        // 判断是否隐藏文字
        if (mHideTitle) {
            for (MaterialItemView materialItemView : mItems) {
                materialItemView.setHideTitle(true);
            }
        }

        // 添加按钮到布局，并注册点击事件
        int size = mItems.size();

        for (int i = 0; i < size; i++) {
            final MaterialItemView tabItem = mItems.get(i);
            tabItem.setChecked(false);
            this.addComponent(tabItem);

            tabItem.setClickedListener(new ClickedListener() {
                @Override
                public void onClick(Component component) {
                    int index = mItems.indexOf(tabItem);
                    if (index >= 0) {
                        setSelect(index, mLastUpX, mLastUpY, true);
                    }
                }
            });
        }

        // 默认选中第一项
        mSelected = DEFAULT_SELECTED;
        mItems.get(DEFAULT_SELECTED).setChecked(true);
    }

    @Override
    public boolean onEstimateSize(int widthMeasureSpec, int heightMeasureSpec) {
        // 排除空状态
        if (mItems == null || mItems.size() <= 0) {
            return false;
        }

        final int width = EstimateSpec.getSize(widthMeasureSpec);
        final int count = getChildCount();
        final int heightSpec = EstimateSpec.getSizeWithMode(MATERIAL_BOTTOM_NAVIGATION_ITEM_HEIGHT, EstimateSpec.PRECISE);

        if (mHideTitle) {
            final int inactiveCount = count - 1;
            final int activeMaxAvailable = width - inactiveCount * MATERIAL_BOTTOM_NAVIGATION_ITEM_MIN_WIDTH;
            final int activeWidth = Math.min(activeMaxAvailable, MATERIAL_BOTTOM_NAVIGATION_ACTIVE_ITEM_MAX_WIDTH);
            final int inactiveMaxAvailable = inactiveCount == 0 ? 0 : (width - activeWidth) / inactiveCount;
            final int inactiveWidth = Math.min(inactiveMaxAvailable, MATERIAL_BOTTOM_NAVIGATION_ITEM_MAX_WIDTH);
            for (int i = 0; i < count; i++) {
                if (i == mSelected) {
                    mTempChildWidths[i] = (int) ((activeWidth - inactiveWidth) * mItems.get(mSelected).getAnimValue() + inactiveWidth);
                } else if (i == mOldSelected) {
                    mTempChildWidths[i] = (int) (activeWidth - (activeWidth - inactiveWidth) * mItems.get(mSelected).getAnimValue());
                } else {
                    mTempChildWidths[i] = inactiveWidth;
                }
            }
        } else {
            final int maxAvailable = width / (count == 0 ? 1 : count);
            final int childWidth = Math.min(maxAvailable, MATERIAL_BOTTOM_NAVIGATION_ACTIVE_ITEM_MAX_WIDTH);
            for (int i = 0; i < count; i++) {
                mTempChildWidths[i] = childWidth;
            }
        }

        mItemTotalWidth = 0;
        for (int i = 0; i < count; i++) {
            final Component child = getComponentAt(i);
            if (child.getVisibility() == HIDE) {
                continue;
            }
            child.estimateSize(EstimateSpec.getSizeWithMode(mTempChildWidths[i], EstimateSpec.PRECISE), heightSpec);
            if (mHideTitle && child instanceof MaterialItemView) {
                // 处理子元素单独的水波纹宽度变化
                ((MaterialItemView) child).onSizeChanged(mTempChildWidths[i], heightSpec);
            }
            ComponentContainer.LayoutConfig params = child.getLayoutConfig();
            params.width = child.getEstimatedWidth();
            mItemTotalWidth += child.getEstimatedWidth();
        }
        setEstimatedSize(widthMeasureSpec, heightMeasureSpec);
        return true;
    }

    @Override
    public boolean onArrange(int left, int top, int right, int bottom) {
        final int count = getChildCount();
        final int width = right - left;
        final int height = bottom - top;
        // 只支持top、bottom的padding
        final int paddingTop = getPaddingTop();
        final int paddingBottom = getPaddingBottom();
        int used = 0;

        if (mItemTotalWidth > 0 && mItemTotalWidth < width) {
            used = (width - mItemTotalWidth) / 2;
        }

        for (int i = 0; i < count; i++) {
            final Component child = getComponentAt(i);
            if (child.getVisibility() == HIDE) {
                continue;
            }
            if (getLayoutDirection() == LayoutDirection.RTL) {
                child.arrange(width - used - child.getEstimatedWidth(), paddingTop, width - used, height - paddingBottom);
            } else {
                child.arrange(used, paddingTop, child.getEstimatedWidth() + used, height - paddingBottom);
            }
            used += child.getEstimatedWidth();
        }

        return true;
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        if (mChangeBackgroundMode) {
            int width = getWidth();
            int height = getHeight();

            Iterator<Oval> iterator = mOvals.iterator();
            while (iterator.hasNext()) {
                Oval oval = iterator.next();
                mPaint.setColor(new Color(oval.color));
                if (oval.radius < oval.maxR && (oval.radius != oval.lastRadius)) {
                    mTempRectF.left = oval.getLeft();
                    mTempRectF.top = oval.getTop();
                    mTempRectF.right = oval.getRight();
                    mTempRectF.bottom = oval.getBottom();
                    canvas.drawOval(mTempRectF, mPaint);
                    oval.lastRadius = oval.radius;
                } else {
                    this.setBackground(ElementUtil.getShapeElementWith6(oval.color));
                    canvas.drawRect(0, 0, width, height, mPaint);
                    iterator.remove();
                }
                getContext().getUITaskDispatcher().asyncDispatch(new Runnable() {
                    @Override
                    public void run() {
                        invalidate();
                    }
                });
            }
        }
    }


    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        if (touchEvent.getAction() == TouchEvent.PRIMARY_POINT_DOWN) {
            mLastUpX = EventUtil.getXInComponent(component, touchEvent);
            mLastUpY = EventUtil.getYInComponent(component, touchEvent);
            return true;
        }
        return false;
    }

    @Override
    public void setSelect(int index) {
        setSelect(index, true);
    }

    @Override
    public void setSelect(int index, boolean needListener) {
        // 不正常的选择项
        if (index >= mItems.size() || index < 0) {
            return;
        }

        Component materialItemView = mItems.get(index);
        setSelect(index, materialItemView.getContentPositionX() + materialItemView.getWidth() / 2f, materialItemView.getContentPositionY() + materialItemView.getHeight() / 2f, needListener);
    }

    @Override
    public void setMessageNumber(int index, int number) {
        mItems.get(index).setMessageNumber(number);
    }

    @Override
    public void setHasMessage(int index, boolean hasMessage) {
        mItems.get(index).setHasMessage(hasMessage);
    }

    @Override
    public void addTabItemSelectedListener(OnTabItemSelectedListener listener) {
        mListeners.add(listener);
    }

    @Override
    public void addSimpleTabItemSelectedListener(SimpleTabItemSelectedListener listener) {
        mSimpleListeners.add(listener);
    }

    @Override
    public void setTitle(int index, String title) {
        mItems.get(index).setTitle(title);
    }

    @Override
    public void setDefaultDrawable(int index, PixelMap drawable) {
        mItems.get(index).setDefaultDrawable(drawable);
    }

    @Override
    public void setSelectedDrawable(int index, PixelMap drawable) {
        mItems.get(index).setSelectedDrawable(drawable);
    }

    @Override
    public int getSelected() {
        return mSelected;
    }

    @Override
    public int getItemCount() {
        return mItems.size();
    }

    @Override
    public String getItemTitle(int index) {
        return mItems.get(index).getTitle();
    }

    @Override
    public boolean removeItem(int index) {
        if (index == mSelected || index >= mItems.size() || index < 0) {
            return false;
        }

        if (mSelected > index) {
            mSelected--;
        }

        this.removeComponentAt(index);
        mItems.remove(index);
        if (mChangeBackgroundMode) {
            mBackgroundColors.remove(index);
        }
        return true;
    }

    @Override
    public void addMaterialItem(int index, PixelMap defaultDrawable, PixelMap selectedDrawable, String title, int selectedColor) {

        final MaterialItemView materialItemView = new MaterialItemView(getContext());
        materialItemView.initialization(title, defaultDrawable, selectedDrawable, mItemTintIcon, mItemDefaultColor, mChangeBackgroundMode ? Color.WHITE.getValue() : selectedColor);

        materialItemView.setChecked(false);
        materialItemView.setRippleColor(0xFFFFFF & selectedColor | 0x22000000);
        materialItemView.setClickedListener(new ClickedListener() {
            @Override
            public void onClick(Component component) {
                int index = mItems.indexOf(materialItemView);
                if (index >= 0) {
                    setSelect(index);
                }
            }
        });

        if (mHideTitle) {
            // 隐藏文字
            materialItemView.setHideTitle(true);
        }

        if (mSelected >= index) {
            mSelected++;
        }

        if (index >= mItems.size()) {
            if (mChangeBackgroundMode) {
                mBackgroundColors.add(selectedColor);
            }
            mItems.add(materialItemView);
            this.addComponent(materialItemView);

        } else {
            if (mChangeBackgroundMode) {
                mBackgroundColors.add(index, selectedColor);
            }
            mItems.add(index, materialItemView);
            this.addComponent(materialItemView, index);
        }
    }

    @Override
    public void addCustomItem(int index, BaseTabItem item) {
        // nothing
    }

    private void setSelect(int index, float touchX, float touchY, boolean needListener) {

        // 重复选择
        if (index == mSelected) {
            if (needListener) {
                for (OnTabItemSelectedListener listener : mListeners) {
                    listener.onRepeat(mSelected);
                }
            }
            return;
        }

        // 记录前一个选中项和当前选中项
        mOldSelected = mSelected;
        mSelected = index;

        // 切换背景颜色
        if (mChangeBackgroundMode) {
            addOvalColor(mBackgroundColors.get(mSelected), touchX, touchY);
        }

        // 前一个选中项必须不小于0才有效
        if (mOldSelected >= 0) {
            mItems.get(mOldSelected).setChecked(false);
        }

        mItems.get(mSelected).setChecked(true);

        if (needListener) {
            // 事件回调
            for (OnTabItemSelectedListener listener : mListeners) {
                listener.onSelected(mSelected, mOldSelected);
            }
            for (SimpleTabItemSelectedListener listener : mSimpleListeners) {
                listener.onSelected(mSelected, mOldSelected);
            }
        }
    }

    /**
     * 添加一个圆形波纹动画
     *
     * @param color   颜色
     * @param centerX X座标
     * @param centerY y座标
     */
    private void addOvalColor(int color, float centerX, float centerY) {
        final Oval oval = new Oval(color, 2, centerX, centerY);

        oval.maxR = getR(centerX, centerY);
        mOvals.add(oval);

        MyValueAnimator valueAnimator = MyValueAnimator.ofFloat(oval.radius, oval.maxR);
        valueAnimator.setCurveType(mInterpolator);
        valueAnimator.setDuration(ANIM_TIME);
        valueAnimator.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float value) {
                oval.radius = value;
            }
        });
        valueAnimator.setStateChangedListener(new CustomAnimatorStateChangedListener() {
            @Override
            public void onStart(Animator animator) {
                invalidate();
            }
        });
        valueAnimator.start();
    }

    /**
     * 以矩形内一点为圆心画圆，覆盖矩形，求这个圆的最小半径
     *
     * @param centerX 横坐标
     * @param centerY 纵坐标
     * @return 最小半径
     */
    private float getR(float centerX, float centerY) {
        int width = getWidth();
        int height = getHeight();

        double r1Square = centerX * centerX + centerY * centerY;
        double r2Square = (width - centerX) * (width - centerX) + centerY * centerY;
        double r3Square = (width - centerX) * (width - centerX) + (height - centerY) * (height - centerY);
        double r4Square = centerX * centerX + (height - centerY) * (height - centerY);

        return (float) Math.sqrt(Math.max(Math.max(r1Square, r2Square), Math.max(r3Square, r4Square)));
    }

    private class Oval {
        int color;
        float radius;
        float centerX;
        float centerY;
        float maxR;
        float lastRadius;

        Oval(int color, float radius, float centerX, float centerY) {
            this.color = color;
            this.radius = radius;
            this.centerX = centerX;
            this.centerY = centerY;
            this.lastRadius = -1;
        }

        float getLeft() {
            return centerX - radius;
        }

        float getTop() {
            return centerY - radius;
        }

        float getRight() {
            return centerX + radius;
        }

        float getBottom() {
            return centerY + radius;
        }
    }

}
